import { getSelector } from '../utils';
import { getLastEvent } from '../utils/getLastEvent';
import checkWhiteScreen from '../utils/checkWhiteScreen';
import { RequestTracker } from '../utils/requestTracker';

/**
 * js错误处理
 */
export class JsError {
    private requestTracker: RequestTracker;
    constructor(requestTracker: RequestTracker) {
        this.requestTracker = requestTracker;
        this.init();
    }
    private init(): void {
        /**
         * 全局监听错误
         */
        window.addEventListener('error', this.handlerGlobalError.bind(this), true);
        window.addEventListener('unhandledrejection', this.handlerPromiseError.bind(this), true);
        /**
         * 延时监听，防止Vue未初始化完成
         */
        setTimeout(() => {
            /**
             * Vue错误处理
             */
            if (window.myVueApp) {
                this.injectVueHandler();
            }
        }, 1000);
    }

    private handlerGlobalError = (event: ErrorEvent) => {
        console.error('js全局错误');
        const target = event.target as HTMLScriptElement | HTMLImageElement | HTMLLinkElement | null;
        let filename;
        // 监听js/css资源加载错误
        if (
            target &&
            (filename = (target as HTMLScriptElement | HTMLImageElement).src || (target as HTMLLinkElement).href)
        ) {
            const log: App.ErrorLog = {
                type: 'error',
                errorType: 'loadResourceError',
                message: `${filename} resource loading fail.`,
                filename,
                tagName: target.tagName,
                selector: getSelector(target)
            };
            this.requestTracker.request(log);
        } else {
            const lastEvent = getLastEvent();
            const isWhiteScreen = checkWhiteScreen();
            const errorLog: App.ErrorLog = {
                type: 'error',
                errorType: 'jsError',
                message: event.message,
                selector: lastEvent ? getSelector() : '',
                filename: event.filename,
                isWhiteScreen,
            };
            if (event.error) {
                const matchResult = event.error.stack.match(/at\s+(.+):(\d+):(\d+)/);
                if (matchResult) {
                    errorLog.position  = `${matchResult[2]}:${matchResult[3]}`;
                    errorLog.stack = event.error.stack;
                }
            }
            this.requestTracker.request(errorLog);
        }
    }

    private handlerPromiseError = (event: PromiseRejectionEvent) => {
        console.error('promise错误');
        const lastEvent = getLastEvent();
        let message;
        const reason = event.reason;
        let filename;
        let line = 0;
        let column = 0;
        let stack = '';
        if (typeof reason === 'string') {
            // 是 Promise reject 抛出的错误，没办法获取到stack等信息
            message = reason;
        } else if (typeof reason === 'object') {
            message = reason.message;
            // 是 Promise中的js代码执行出错
            if (reason.stack) {
                const matchResult = reason.stack.match(/at\s+(.+):(\d+):(\d+)/);
                if (matchResult) {
                    filename = matchResult[1];
                    line = matchResult[2];
                    column = matchResult[3];
                    stack = reason.stack
                }
            }
        }
        const { baseURL, url, method, data } = reason.config;
        const errorLog: App.ErrorLog = {
            type: 'error',
            errorType: 'promiseError',
            message,
            selector: lastEvent ? getSelector() : '',
            isWhiteScreen: checkWhiteScreen(),
            filename,
            position: `${line}:${column}`,
            stack: stack,
            reqUrl: baseURL + url.slice(1),
            xhrData: {
                eventType: 'error',
                url: baseURL + url.slice(1),
                method,
                data,
                status: reason.request.status,
                response: reason.request.response
            }
        };
        this.requestTracker.request(errorLog);
    }

    private injectVueHandler(): void {
        window.myVueApp.config.errorHandler = (err: any, vm: any, info: any) => {
            console.error('vue3错误捕获');
            console.error(err);
            const lastEvent = getLastEvent(); // 监听到错误后，获取最后一个交互事件
            const isWhiteScreen = checkWhiteScreen(); // 检测是否白屏
            const errorLog: App.ErrorLog = {
                type: 'error',
                errorType: 'jsError',
                message: err.message,
                selector: lastEvent ? getSelector(lastEvent.target as Element) : '',
                isWhiteScreen
            };
            const matchResult = err.stack.match(/at\s+(.+):(\d+):(\d+)/);
            if (matchResult) {
                errorLog.filename = matchResult[1];
                errorLog.position  = `${matchResult[2]}:${matchResult[3]}`;
                errorLog.stack = err.stack;
            }
            this.requestTracker.request(errorLog);
        }
    }
}
